import {
  AfterViewInit,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ContentChild,
  ContentChildren,
  ElementRef,
  EventEmitter,
  Input,
  NgZone,
  OnChanges,
  OnDestroy,
  OnInit,
  Output,
  QueryList,
  Renderer2,
  SimpleChanges,
  TemplateRef,
  TrackByFunction,
  ViewChild,
  ViewEncapsulation,
} from '@angular/core';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { SelectionModel } from '@angular/cdk/collections';
import { fromEvent, merge, Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, delay, filter, map, startWith, switchMap, takeUntil, tap } from 'rxjs/operators';
import {
  filter as _filter,
  forEach as _forEach,
  map as _map,
  pickBy as _pickBy,
  toNumber as _toNumber,
} from 'lodash';
import {
  flatMap as lvFlatMap,
  I18NService,
  InputBoolean,
  InputNumber,
  isEmpty as lvIsEmpty,
  LvConfig,
  LvSelectSM,
  LvSizeLSD,
  ResizeObserverService,
  result as lvResult,
  TypeUtils,
} from '../core/api';
import { SortConfig, SortDirective } from '../sort/api';
import { PageConfig, PaginatorComponent } from '../pagination/api';
import { TableDataSource } from './table-data-source';
import { DatatableService } from './datatable.service';
import { initDatatableDataError, initHtmlStructureError } from './datatable-errors';
import { TheadComponent } from './thead.component';
import { ThComponent } from './th.component';
import { TdComponent } from './td.component';
import { VirtualScrollDirective } from './virtual-scroll.directive';
import { ContentScrollType, ResizeType, TableFilterConfig as FilterConfig } from './interface';
import { ColgroupDirective } from './colgroup.directive';

export const DatatableHostConfig = {
  '[class.lv-table]': 'true',
  '[class.lv-table-default]': 'lvSize === "default"',
  '[class.lv-table-large]': 'lvSize === "large"',
  '[class.lv-table-small]': 'lvSize === "small"',
  '[class.lv-table-stripe]': 'lvStripe',
  '[class.lv-table-multi-group]': 'lvMultiGroup',
  '[class.lv-table-bordered]': 'lvBordered',
};

interface ResizeResType {
  data?: { width?: number; initWidth?: number; offset?: number; totalOffset?: number; type?: string };
  index?: number;
}

const DEFAULT_BAR_WIDTH = 8;

@Component({
  selector: 'lv-datatable',
  templateUrl: './datatable.component.html',
  providers: [DatatableService],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: DatatableHostConfig,
})
export class DatatableComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
  // 输入输出属性
  @Input() lvKey: any; // 唯一标识
  @Input() lvData: any[]; // 源数据
  @Input() lvSelection: any[]; // 默认选中的项
  @Input() @InputBoolean() lvFake; // 是否为傀儡table，如果为true表格无数据将不显示
  @Input() lvSortDirct: SortDirective; // 关联sort组件
  @Input() @InputBoolean() lvBordered; // 是否显示边框

  @Input() @InputBoolean() lvShowHeader; // 是否显示头
  @Input() @InputBoolean() lvShowFooter; // 是否显示尾

  @Input() lvSize: LvSizeLSD = 'default'; // size
  @Input() @InputBoolean() lvAsync = false; // 是否为异步表格
  @Input() lvPaginator: PaginatorComponent; // 关联分页组件
  @Input() @InputBoolean() lvStripe = false; // 条纹
  // resize
  @Input() @InputBoolean() lvResize = false; // 是否可以调整表头宽度
  @Input() lvResizeMode: ResizeType = 'fit'; // 拖动模式，自适应|拓宽table
  @Input() @InputBoolean() lvLoading = false; // 加载中

  @Input() lvDataSource: TableDataSource<any>; // 隐藏功能
  @Input() @InputBoolean() lvMultiGroup = false; // 分组
  @Input() @InputNumber() lvVirtualItemSize = 40; // 每行高度
  @Input() lvVirtualTrackBy: TrackByFunction<any>; // 虚拟滚动的trackBy
  // selection
  @Input() lvSelectionMode: LvSelectSM = 'single'; // 选择模式

  @Output() lvScrollEnd = new EventEmitter<any>(); // 虚拟滚动到底部时回调事件
  // == virtual scroll
  @Input() @InputBoolean() lvVirtualScroll = false; // 虚拟滚动

  @Input() @InputNumber() lvVirtualMaxBuffer = 200; // 渲染新项时最大缓冲高度
  @Input() @InputNumber() lvVirtualMinBuffer = 100; // 视图之外渲染的最小缓冲区数，若缓存区数量低于此数量将渲染新项
  @Input() lvCompareWith: string | ((o: any) => any); // data被选中的比较方式，默认地址比较

  @Output() lvScrollingChange = new EventEmitter<any>(); // 虚拟滚动中回调事件

  @Output() lvSelectionChange = new EventEmitter<any[]>(); // 选择项变化回调

  @Output() lvStateChange = new EventEmitter<{ [key: string]: any }>(); // 状态变化回调

  @ViewChild('theadElement', { static: false }) theadElement: ElementRef;
  @ViewChild('tableContent', { static: true }) _tableContentElementRef: ElementRef;
  @ContentChildren(ThComponent, { descendants: true }) thComponents: QueryList<ThComponent>; // 所以不支持嵌条table
  @ContentChildren(TdComponent, { descendants: true }) tdComponents: QueryList<TdComponent>; // 所以不支持嵌条table
  @ViewChild('scrollComponent', { static: false }) _scrollViewport: CdkVirtualScrollViewport;
  @ContentChild(ColgroupDirective, { static: false }) _colgroupDirective: VirtualScrollDirective;
  @Input() @InputBoolean() lvKeepWhiteSpace: boolean = LvConfig.dataTableOptions.lvKeepWhiteSpace; // 是否保留字符的所有空格
  @ContentChild(VirtualScrollDirective, { static: false }) _virtualTemplate: VirtualScrollDirective; // 特定Template
  @Input() @InputNumber() lvResizeMinWidth: number = LvConfig.dataTableOptions.lvResizeMinWidth || 96; // 拖动最小宽度
  @ViewChild('tableBodyElement', { read: ElementRef, static: false }) _tableBodyElementRef: ElementRef;

  @ViewChild('tableHeaderElement', { read: ElementRef, static: false }) _tableHeaderElementRef: ElementRef;
  @ViewChild(CdkVirtualScrollViewport, { read: ElementRef, static: false }) _tableVirtualBodyElementRef: ElementRef;
  @Input() lvNoResult: { icon?: string; description?: string | TemplateRef<void> } | any = {
    icon: 'lv-icon-no-data',
    description: this.i18n.get('emptyData'),
  };

  @Input() lvScroll: { x?: string | null; y?: string | null; autosize?: boolean } = {
    x: null,
    y: null,
    autosize: false, // 当scroll.y>tbody.height,是否自动按内容适配高度
  }; // x,y 显示宽高，超过出现滚动

  states; // table 所有条件
  scrollY = '';
  traversal = false; // 数据处理是否遍历子项
  ths: string[] = [];
  // 外部可访问变量
  typeUtils = TypeUtils;
  renderData: any[] = [];
  initThs: string[] = [];
  theadComponent: TheadComponent;
  _selection: SelectionModel<any>;
  _dataSource: TableDataSource<any>;
  timer: ReturnType<typeof setTimeout>;
  filteredData: { [key: string]: any };

  viewCompleted$ = new Subject<void>();
  selectionChange$ = new Subject<void>();
  renderDataChange$ = new Subject<any[]>();

  public scrollBarWidth = 0;
  private colsLength = 0;
  private tableResizeObserver;
  private scrollBarResizeObserver;
  private isColGroupInit = true;
  private isThLengthChanged = true;
  private tableContentElement: Element; // 用于计算表格初始宽度
  // 内部变量
  private _destroy$ = new Subject<void>();
  private filterData$ = Subscription.EMPTY;
  private _stateChange$ = Subscription.EMPTY;

  private _renderChange$ = Subscription.EMPTY;
  private viewportRefresh$ = new Subject<boolean>();
  private virtualRenderedRange$ = Subscription.EMPTY;
  private resizableInfo = {
    initTableWidth: 0,
    lastColIndex: 0,
    initLastColWidth: 0,
    nextColIndex: 0,
    initNextColWidth: 0,
    initResizeColsWidth: 0,
  };

  constructor(
    public ngZone: NgZone,
    public _cdr: ChangeDetectorRef,
    public datatableService: DatatableService,
    public _renderer: Renderer2,
    public resizeObserverService: ResizeObserverService,
    public elementRef: ElementRef,
    public i18n: I18NService,
  ) {
    this.catchTableError();
    this.datatableService.setParentComponent(this);
  }

  get selectionMultiMode(): boolean {
    return this.lvSelectionMode === 'multiple';
  }

  get _tableActualHeaderElement(): HTMLElement {
    return (this._tableHeaderElementRef || ({} as any)).nativeElement;
  }

  get _tableActualBodyElement(): HTMLElement {
    return this._tableBodyElementRef?.nativeElement || this._tableVirtualBodyElementRef?.nativeElement;
  }

  ngOnInit(): void {
    this.initData();
  }

  initData(): void {
    this.setSelection();
  }

  // 表头默认判断的全选列表，即渲染的列表
  getUnfoldRenderData(): any[] {
    return this.renderData;
  }

  /**
   * 获取data主键，默认为对象地址
   * @param source 源数据
   */
  result(source: any): any {
    return lvResult(source, this.lvCompareWith);
  }

  // 获取全部选中项-全部
  getSelection(): any[] {
    return this._selection?.selected;
  }

  isEmpty(value): boolean {
    return lvIsEmpty(value) || value === 'undefined';
  }

  // 设置thead
  setTheadComponent(source: TheadComponent): void {
    this.theadComponent = source;
  }

  // 判断当前行是否在当前渲染列表中
  getRowInRender(source: any): boolean {
    return this.renderData.includes(source);
  }

  /**
   * 手动清除page
   * @param key 不传代表清除所有，传代表清除指定key的排序,支持批量清除
   */
  removePage(): void {
    this.datatableService.removePaginatorState();
    this._rePage();
  }

  bulkResult(source: any[]): any {
    return _map(source, (item): any => this.result(item));
  }

  // 是否为全选-当前渲染
  isAllSelected(): boolean {
    return this.getRenderSelection().length === this.renderData.length;
  }

  // 是否有选中-当前渲染
  isHalfSelected(): boolean {
    return !this.isAllSelected() && !!this.getRenderSelection().length;
  }

  resultToDataMap(source: any[]): Map<any, any> {
    return lvFlatMap(source, this.lvCompareWith, false);
  }

  // sort
  sort(source: SortConfig = {}): void {
    this.datatableService.setSortState(source);
    this._reSort();
  }

  // page
  page(source: PageConfig): void {
    this.datatableService.setPaginatorState(source);
    this._rePage();
  }

  getFilterValue(key?: string): any {
    const data: any = this.getFilter(key);
    return _map(data, 'value');
  }

  // 选中
  selectSelection<T>(value: T): void {
    this.refreshSelectionMap([value]);
    this._selection.select(value);
  }

  catchTableError(): void {
    if (this.lvData && this.lvDataSource) {
      throw initDatatableDataError();
    }
  }

  _reSort(): void {
    const data = this.datatableService.getSortState();
    this._dataSource.setSortState(data);
  }

  // 反选
  deselectSelection<T>(value: T): void {
    this.refreshSelectionMap([value]);
    this._selection.deselect(value);
  }

  isScrollable(): boolean {
    return this.lvVirtualScroll || this.lvScroll?.x !== null || this.lvScroll?.y !== null;
  }

  _reFilter(): void {
    const data = this.datatableService.getFilterState();
    this._dataSource.setFilterState(data);
  }

  _rePage(): void {
    const data = this.datatableService.getPaginatorState();
    this._dataSource.setPaginatorState(data);
  }

  // 清除所有-全部
  clearSelection(emit: boolean = true): void {
    this._selection?.clear();
    if (emit) {
      this.updateSelection();
    }
  }

  // 清除所有-当前渲染 NEED_DO:按renderData计算有些不合理
  clearRenderSelection(emit: boolean = true): void {
    this.deleteSelection(this.renderData);
    if (emit) {
      this.updateSelection();
    }
  }

  // 直接使用DataSource初始化
  setDataSource(data: any): void {
    this._dataSource = data.currentValue;
    this._subscribeRenderStateChange(this._dataSource);
  }

  // 切换
  toggleSelectionByStatus<T>(value: T, status: boolean): void {
    status ? this.selectSelection(value) : this.deselectSelection(value);
  }

  /**
   * 手动清除sort
   * @param key 不传代表清除所有，传代表清除指定key的排序
   * 目前仅只考虑一列排序
   */
  removeSort(key?: string): void {
    key ? this.datatableService.removeSortState(key) : this.datatableService.clearSortState();
    this._reSort();
  }

  // 是否选中
  isSelected<T>(value: T): boolean {
    const selected = this.getSelection();
    return selected.some((item): boolean => this.result(item) === this.result(value));
  }

  ngOnDestroy(): void {
    this._destroy$.next();
    this._destroy$.complete();
    this.virtualRenderedRange$.unsubscribe();
    this.tableResizeObserver?.unobserve();
    this.scrollBarResizeObserver?.unobserve();
  }

  // 切换选择项
  toggleSelection<T>(value: T, emit: boolean = true): void {
    this.refreshSelectionMap([value]);
    this._selection.toggle(value);
    if (emit) {
      this.updateSelection();
    }
  }

  // 批量选中项-全部
  bulkSelection<T>(values: T[], emit: boolean = true): void {
    this.refreshSelectionMap(values);
    this._selection.select(...values);
    if (emit) {
      this.updateSelection();
    }
  }

  // 批量删除项
  deleteSelection<T>(values: T[], emit: boolean = true): void {
    this.refreshSelectionMap(values);
    this._selection.deselect(...values);
    if (emit) {
      this.updateSelection();
    }
  }

  _updateRenderData(source: any[]): void {
    this.renderData = [...source];

    // 更新renderData的显示
    this._cdr.markForCheck();
    this.renderDataChange$.next(this.renderData);
  }

  // 获取当前渲染项选中项-当前渲染
  getRenderSelection(): any[] {
    const dataMap = this.resultToDataMap(this.renderData);
    return _filter(this.getSelection(), (item): boolean => dataMap.has(this.result(item)));
  }

  getFilter(key: string): FilterConfig;
  // eslint-disable-next-line no-dupe-class-members
  getFilter(): FilterConfig[];
  /**
   * 选择
   * 不排除disable场景NEED_DO:
   */
  // eslint-disable-next-line no-dupe-class-members
  getFilter(key?: string): FilterConfig | FilterConfig[] {
    const filters = this.datatableService.getFilterState();
    if (key) {
      return filters.get(key);
    }
    return Array.from(filters.values());
  }

  // 普通data用法 默认配置好dataSource
  setData(data?: any): void {
    // 避免重复初始化
    if (!this._dataSource) {
      this._dataSource = new TableDataSource(data, this.lvAsync, this.traversal);
      this._subscribeRenderStateChange(this._dataSource);
    } else {
      this._dataSource.data = data;
    }
  }

  // 暂不删除 NEED_DO:暂不删除，另一方案会使用。最终方案对齐后再根据情况删除
  reShowScrollView(): void {
    if (this._scrollViewport) {
      const tableView = this._scrollViewport.elementRef.nativeElement.querySelector('table');
      tableView.style.display = 'none';
      setTimeout((): void => {
        tableView.style.display = '';
      });
    }
  }

  /**
   * 订阅virtual scroll 的scroll事件
   * 记录滚动到底部时，触发回调
   * NEED_DO: 追加的数据，显示错位
   */
  scrolledIndexChange(e): void {
    const view = this._scrollViewport;
    const { end } = view.getRenderedRange();
    const total = view.getDataLength();

    this.lvScrollingChange.emit({ source: view, index: e });
    if (end === total) {
      this.lvScrollEnd.emit();
    }
  }

  /**
   * 手动清除filter
   * @param key 不传代表清除所有，传代表清除指定key的排序,支持批量清除
   */
  removeFilter(key?: string | string[]): void {
    if (!key) {
      this.datatableService.clearFilterState();
    } else {
      const keyArr = typeof key === 'string' ? [key] : key;
      keyArr.forEach((o): void => this.datatableService.removeFilterState(o));
    }
    this._reFilter();
  }

  /**
   * 获取滚动条宽度
   * @returns 滚动条宽度
   */
  getScrollBarWidth(): number {
    let defaultBarWidth: number = 0;
    if (!this.lvVirtualScroll || !this.renderData?.length) {
      if (this._tableBodyElementRef) {
        defaultBarWidth =
          this._tableBodyElementRef.nativeElement.offsetWidth - this._tableBodyElementRef.nativeElement.clientWidth;
      }
    } else {
      if (this._tableVirtualBodyElementRef) {
        defaultBarWidth =
          this._tableVirtualBodyElementRef.nativeElement.offsetWidth -
          this._tableVirtualBodyElementRef.nativeElement.clientWidth;
      }
    }

    return defaultBarWidth || DEFAULT_BAR_WIDTH;
  }

  /**
   * 初始化选中项
   * data 变更触发 selection 初始化；
   * data 不变更 变更 selection，不重新初始化（暂时不考虑）
   * 发布一个selection change流th td监听变更选中
   */
  setSelection(): void {
    this.clearSelection(false);
    this._selection = new SelectionModel(this.selectionMultiMode, this.lvSelection);
    // 更新分页器选中项条数
    this.datatableService.updatePaginatorSelected(this._dataSource?.paginator, this.lvSelection?.length);
    this.selectionChange$.next();
  }

  // 更新selectionModule
  refreshSelectionModule(): any {
    const dataMap = this.resultToDataMap(this._dataSource.data);
    const selected = this.lvSelection;
    const usefulSelected = _map(selected, (item): any => dataMap.get(item) || item);
    this.clearSelection(false);
    this.bulkSelection(usefulSelected, false);
    return usefulSelected;
  }

  // 刷新selectionModel,记录的selection地址变更 - NEED_DO： 优化
  refreshSelectionMap<T>(values: T[]): void {
    const selected = this.getSelection();
    _forEach(values, (item): void => {
      const temp = _filter(selected, (o): boolean => this.result(o) === this.result(item));
      if (!temp.length) {
        return;
      }
      this._selection.deselect(temp[0]);
      this._selection.select(item);
    });
  }

  // filter
  filter(source: FilterConfig): void {
    const _source = {
      ...{ filterMode: 'startsWith', caseSensitive: false },
      ...source,
    } as FilterConfig;
    if (this.hasValue(source.value)) {
      this.datatableService.setFilterState(_source);
    } else {
      this.datatableService.removeFilterState(_source.key);
    }
    this._reFilter();
  }

  /**
   * 手动触发虚拟滚动里的视图重排
   */
  reflowScrollView(): void {
    if (this._scrollViewport) {
      const tableView = this._scrollViewport.elementRef.nativeElement.querySelector('table');
      const td = tableView.querySelector('td');
      // 使用DOM树变更触发一次强制的表格重绘
      if (td) {
        const el = document.createElement('span');
        td.appendChild(el);
        setTimeout((): HTMLSpanElement => td.removeChild(el));
      }
    }
  }

  convertState(source: { [key: string]: any }): { [key: string]: any } {
    const state = {
      sort: source.sort,
      sortState: source.sortState && Array.from(source.sortState.values()).pop(),
      filterState: source.filterState && Array.from(source.filterState.values()),
      paginator: source.paginator,
      paginatorState: source.paginatorState,
    };
    return _pickBy(state, (item) => item);
  }

  _setTableContentScroll(status: ContentScrollType): void {
    const tableRef = this._tableContentElementRef.nativeElement;
    const classMap = {
      0: 'lv-table-content-scroll-left',
      1: 'lv-table-content-scroll-center',
      2: 'lv-table-content-scroll-right',
    };
    _forEach(classMap, (value: string): void => {
      this._renderer.removeClass(tableRef, value);
    });
    this._renderer.addClass(tableRef, classMap[status]);
  }

  updateSelection(): void {
    if (!this._selection) {
      return;
    }
    const selection = this.getSelection();

    if (this.lvSelection) {
      this.lvSelection.length = 0;
      // 不改变lvSelection地址，避免lvSelection双向绑定，修改lvSelection地址触发onChanges
      this.lvSelection.push(...selection);
    } else {
      this.lvSelection = [...selection];
    }

    // 更新分页器选中项条数
    this.datatableService.updatePaginatorSelected(this._dataSource.paginator, this.lvSelection.length);
    this.lvSelectionChange.emit(this.lvSelection);
  }

  /**
   * 异步：data == renderData, state不影响data
   */
  _asyncChange(dataSource: TableDataSource<any>): void {
    const dataStream = dataSource.connect();
    const stateStream = dataSource.states();
    const filterStream = dataSource.filters();

    this._renderChange$.unsubscribe();
    this._renderChange$ = dataStream.pipe(takeUntil(this._destroy$)).subscribe((res): void => {
      this._updateRenderData(res || []);
    });

    this._stateChange$.unsubscribe();
    this._stateChange$ = stateStream.pipe(takeUntil(this._destroy$)).subscribe((stateSource): void => {
      // 发布state回调
      this.lvStateChange.emit(this.convertState(stateSource));
    });

    this.filterData$.unsubscribe();
    this.filterData$ = filterStream.pipe(takeUntil(this._destroy$)).subscribe((res): void => {
      this.filteredData = res;
    });
  }

  initScrollBar(): void {
    this.scrollBarWidth = this.getScrollBarWidth();
    this.scrollBarResizeObserver = this.resizeObserverService.observe(this.tableContentElement, (): void => {
      this.scrollBarWidth = this.getScrollBarWidth();
    });
  }

  // 全选-当前渲染  废弃 当有disable场景不能解决
  ngAfterViewInit(): void {
    this.tableContentElement = this._tableContentElementRef?.nativeElement;
    this.initScrollBar();
    this.syncTableScroll();
    this.calculateVirtualContainerHeight();

    if (this.lvResize) {
      // 在外部设置lvScroll后，表格宽度需要按照配置的lvScroll渲染后再计算，之前的promise时lvScroll最新值未渲染到视图故改为setTimeout NEED_DO: layout导致table需要延迟计算，需要要移除
      setTimeout((): void => {
        this.setInitTableWidth(this.isScrollable());
        this.colsLength = this.thComponents?.length;
        this.setColgroup();
        this.isColGroupInit = false;
      });
      if (this.tableContentElement) {
        this.tableResizeObserver = this.resizeObserverService.observe(this.tableContentElement, (): void => {
          this.setInitTableWidth(this.isScrollable());
          this.setLastResizableCol();
        });
      }
    }
  }

  calculateThs(res: QueryList<ThComponent>): HTMLElement[] {
    const cacheThLength = res.length;
    const currentThComponents = res.toArray().map((item: ThComponent): any => item.elementRef.nativeElement);
    if (this.timer) {
      clearTimeout(this.timer);
    }
    // 除初始化外，th变更时不是由于length变更引起，则无需对th的宽度进行重新计算
    if (this.isColGroupInit) {
      this.ths = currentThComponents.map((element): string => this.getWidth(element));
      this.initThs = currentThComponents.map((element): `${number}px` => `${element.getBoundingClientRect().width}px`);
      this.setLastResizableCol();
    } else if (this.colsLength !== cacheThLength) {
      this.isThLengthChanged = true;
      this.ths = [];
      this.timer = setTimeout((): void => {
        this.ths = currentThComponents.map((element): string => this.getWidth(element));
        this.initThs = currentThComponents.map(
          (element): `${number}px` => `${element.getBoundingClientRect().width}px`,
        );
        this.timer = null;
        this.setLastResizableCol();
        this._cdr.markForCheck();
      });
    } else {
      return currentThComponents;
    }
    return currentThComponents;
  }

  ngOnChanges(changes: SimpleChanges): void {
    const {
      lvData: dataChange,
      lvDataSource: dataSourceChange,
      lvSelection: selectionChange,
      lvSelectionMode: selectionModeChange,
      lvCompareWith: compareWithChange,
      lvPaginator: paginatorChange,
      lvSortDirct: sortDirectChange,
      lvAsync: asyncChange,
      lvNoResult: emptyChange,
      lvResizeMode: resizeModeChange,
    } = changes;

    // 初始化dataSource
    if (dataSourceChange) {
      // 原生dataSource用法
      this.setDataSource(dataSourceChange.currentValue);
    } else if (dataChange) {
      // 普通data用法 默认配置好dataSource
      this.setData(dataChange.currentValue);
    }

    if (!this._dataSource) {
      this.setData();
    }

    const flag = dataSourceChange || dataChange || selectionChange || selectionModeChange || compareWithChange;
    if (flag) {
      // 更新selection
      this.setSelection();
      this.refreshSelectionModule();
    }

    // 绑定分页
    if (paginatorChange) {
      this._dataSource.paginator = paginatorChange.currentValue;
    }

    // 绑定排序 NEED_DO: page sort 需要支持init流
    if (sortDirectChange) {
      this._dataSource.sort = sortDirectChange.currentValue;
    }

    if (asyncChange && !asyncChange.firstChange) {
      this._dataSource.async = this.lvAsync;
    }

    if (emptyChange) {
      if (!emptyChange.currentValue) {
        return;
      }
      const { icon = 'lv-icon-no-data', description = this.i18n.get('emptyData') } = emptyChange.currentValue || {};

      this.lvNoResult = { icon, description };
    }

    if (resizeModeChange && !resizeModeChange.currentValue) {
      this.lvResizeMode = 'fit';
    }
  }

  /**
   * 设置colgroup
   * 保证th、td宽度一致，同时针对虚拟滚动table列拖动
   * 注：列过少table过宽（列之和小于table宽）时存在和期望不符现象
   */
  setColgroup(): void {
    this.thComponents.changes
      .pipe(
        startWith(this.thComponents),
        switchMap((res): Observable<ResizeResType> => {
          const currentThComponents = this.calculateThs(res);
          // 当th存在动态显示时需要触发一次变更
          Promise.resolve().then((): void => {
            this.colsLength = res.length;
            this._cdr.detectChanges();
          });
          this.reflowScrollView();
          return merge(
            ...res.toArray().map(
              (item: ThComponent, index: number): Observable<{ data: any; index: number }> =>
                item.getResizeOffset().pipe(
                  filter((data: ResizeResType['data']): boolean => {
                    const { offset, type, initWidth } = data || {};
                    if (type === 'mousedown') {
                      if (this.lvResizeMode === 'fit') {
                        this.setNextResizableCol(index, initWidth);
                      }
                      if (this.isThLengthChanged) {
                        this.ths = currentThComponents.map(
                          (element): `${number}px` => `${element.getBoundingClientRect().width}px`,
                        );
                        this.isThLengthChanged = false;
                      }
                    }
                    return !!offset;
                  }),
                  map((data): { data: any; index: number } => ({ data, index })),
                ),
            ),
          ) as Observable<ResizeResType>;
        }),
        takeUntil(this._destroy$),
      )
      .subscribe((res): void => {
        this.setResizeColsWidth(res);
      });
  }

  /**
   * 更新表头表体scroll，滚动表体同时更新表头scroll
   * 表体：虚拟滚动、非虚拟滚动
   * 只是用scroll样式时，出现横向滚动场景，需要监听滚动，执行表头和表体同时滚动
   */
  syncTableScroll(): void {
    // 虚拟滚动或可能出现横向滚动（配置了横向滚动或者拖拽模式为expand）的场景，需要监听滚动，执行表头和表体同时滚动
    if (!this.isScrollable()) {
      return;
    }
    this.ngZone.runOutsideAngular((): void => {
      let preStatus: ContentScrollType;
      this.renderDataChange$
        .pipe(
          delay(1),
          tap((): void => {
            // 存在data跳变到无数据时，body重排重置起始位置
            const isBody = this._tableActualBodyElement;
            const isNullData = !this.renderData.length;
            if (isNullData && this._tableHeaderElementRef?.nativeElement) {
              // 表头、表体还原原位置
              this._tableHeaderElementRef.nativeElement.scrollLeft = 0;
              isBody.scrollLeft = 0;
            }
          }),
          switchMap((): Observable<Event> => fromEvent(this._tableActualBodyElement, 'scroll')), // 优化过滤调y的消息 NEED_DO: 优化过滤调y的消息，distinctUntilChanged无效
          map((e: MouseEvent): EventTarget => e.target),
          takeUntil(this._destroy$),
        )
        .subscribe((res: Element): void => {
          if (!this._tableHeaderElementRef) {
            return;
          }
          this._tableHeaderElementRef.nativeElement.scrollLeft = res.scrollLeft;
          const offsetX = res.scrollWidth - res.clientWidth;
          // 设置是不是x轴滚动左中右
          let status: ContentScrollType;
          if (res.scrollLeft === 0) {
            // 左
            status = 0;
          } else if (offsetX === res.scrollLeft) {
            // 右
            status = 2;
          } else {
            // 中
            status = 1;
          }
          // 去重
          if (preStatus === status) {
            return;
          }
          this._setTableContentScroll(status);
          preStatus = status;
        });
    });
  }

  /**
   * 在虚拟滚动场景，自动计算body区，根据内容自适应高度
   */
  calculateVirtualContainerHeight(): void {
    if (!this.lvVirtualScroll || !this.lvScroll?.autosize) {
      return;
    }

    const getContainerHeight = (viewportDom): string => {
      const { height, width: viewportWidth } = viewportDom.getBoundingClientRect();
      // 虚拟滚动容器高度采用lvScroll.y和viewport容器高度较大值，适配视图未加载完成的情况(tabs切换未使用懒加载)
      const scrollY = parseFloat(this.lvScroll.y);
      const viewportHeight = Number.isNaN(scrollY) ? height : Math.max(height, scrollY);

      const contentDom = viewportDom.querySelector('.cdk-virtual-scroll-content-wrapper');
      const { width: contentWidth } = contentDom.getBoundingClientRect();
      const { height: trHeight } = viewportDom.querySelector('.lv-table-tr')?.getBoundingClientRect() || {};

      // 虚拟滚动在当前周期中获取到的DOM节点高度不准确，根据行高(适配视图未加载完的情况：采用lvVirtualItemSize与tr容器高度较大的值)和renderData计算content高度。
      const contentHeight = Math.max(this.lvVirtualItemSize, trHeight) * this.renderData.length;

      // 判断是否存在x轴滚动条， 虚拟滚动最大内容宽度 = viewportWidth - 减去滚动条宽度
      const offset = contentWidth > viewportWidth - this.scrollBarWidth ? this.scrollBarWidth : 0; // 滚动条宽度偏移量
      // 比较配置的lvScroll.y和实际撑开的内容高度
      return `${Math.min(viewportHeight, Number(contentHeight) + offset)}px`;
    };

    const refreshViewportSize = (): void => {
      setTimeout((): void => {
        // refresh its internal size values after changing the container height
        this._scrollViewport?.checkViewportSize();
      }, 300);
    };

    merge(this.renderDataChange$, this.viewportRefresh$)
      .pipe(
        startWith(this.renderData),
        // distinct((o) => o?.length),  // NEED_DO: 存在同长度不同高度场景
        debounceTime(0), // 视图渲染后
        takeUntil(this._destroy$),
      )
      .subscribe((): void => {
        this.ngZone.runOutsideAngular((): void => {
          const viewportDom = this._scrollViewport?.elementRef.nativeElement;
          if (!viewportDom) {
            return;
          }

          // 还原原高度来比较
          viewportDom.style.height = this.lvScroll?.y;
          viewportDom.style.height = getContainerHeight(viewportDom);
          refreshViewportSize();
        });
      });
  }

  private hasValue(value): boolean {
    return TypeUtils.isArray(value) ? value.length : value;
  }

  /**
   * 订阅renderData change，state change
   * @param dataSource
   * TIPS:
   * state change: 内部触发的change不推送消息（filter触发page变化）
   * 区分同步、异步
   */
  private _subscribeRenderStateChange(dataSource: TableDataSource<any>): void {
    if (!dataSource) {
      return;
    }

    this._asyncChange(dataSource);
  }

  /**
   * 设置表格初始宽度
   */
  private setInitTableWidth(isScrollable = false): void {
    const { width: tableWidth } = this.tableContentElement.getBoundingClientRect();
    this.resizableInfo.initTableWidth = Math.floor(isScrollable ? (tableWidth || 0) - this.scrollBarWidth : tableWidth);
  }

  /**
   * 设置 fit 拖拽模式列宽
   * 只改变拖拽列和后一个可拖拽列的宽度，此消彼长，整体表格宽度不改变，最后一列不可拖拽
   */
  private setFixResizeColWidth({ width, index }): void {
    const { lastColIndex, nextColIndex, initResizeColsWidth } = this.resizableInfo;
    if (index === lastColIndex) {
      return;
    }

    const nextColWidth = initResizeColsWidth - width;
    if (nextColWidth < this.lvResizeMinWidth) {
      return;
    }

    // 保留2位小数，不做四舍五入，避免总宽度超出容器最大宽度出现x轴滚动条
    this.ths[index] = `${Math.floor(width * 100) / 100}px`;
    this.ths[nextColIndex] = `${Math.floor(nextColWidth * 100) / 100}px`;
  }

  /**
   * 设置拖拽后的列宽
   * @param res 拖拽列数据
   */
  private setResizeColsWidth(res: ResizeResType = {}): void {
    const { index, data } = res;
    if (index !== -1) {
      // clientWidth获取到的宽度是四舍五入后的，而getBoundingClientRect带小数值
      const { width, offset } = data;

      if (this.lvResizeMode === 'fit') {
        this.setFixResizeColWidth({ width, index });
      } else {
        this.setExpandResizeColWidth({ width, offset, index });
      }
    }

    this._cdr.detectChanges();
  }

  /**
   * 设置可拖拽的最后一列信息：index、初始宽度
   */
  private setLastResizableCol(): void {
    const thComponents = this.thComponents.toArray();
    for (let index = thComponents.length - 1; index >= 0; index--) {
      const item = thComponents[index];
      if (item.isResizable) {
        if (item.elementRef.nativeElement.tagName !== 'TH') {
          throw initHtmlStructureError();
        }
        this.resizableInfo.lastColIndex = index;
        this.resizableInfo.initLastColWidth = item.elementRef.nativeElement.getBoundingClientRect().width;
        return;
      }
    }
  }

  /**
   * 获取th宽度
   * eleWidth: th上使用width和style配置的宽度
   * 场景1: eleWidth有值且能转换为数值，则直接使用
   * 场景2: eleWidth有值且不能转换为数值, expand模式下，使用clientWidth；fit模式下，使用传入值或者不设置
   * 场景3: eleWidth没有值，使用clientWidth
   * @param ele th的节点
   */
  private getWidth(ele): string {
    const initWidth = ele.width || ele.style.width;
    let width;

    const clientWidth = `${ele.getBoundingClientRect().width || this.lvResizeMinWidth}px`;

    if (this.typeUtils.isNotNil(initWidth) && !this.isEmpty(initWidth) && initWidth !== 'undefined') {
      if (this.typeUtils.isValidNumber(_toNumber(initWidth))) {
        width = `${_toNumber(initWidth)}px`;
      } else {
        width = initWidth.indexOf('%') && this.lvResizeMode === 'expand' ? clientWidth : initWidth;
      }
    } else {
      width = 'auto';
    }

    return width;
  }

  /**
   * 设置 expand 拖拽模式列宽
   */
  private setExpandResizeColWidth({ width, index, offset }): void {
    const { lastColIndex } = this.resizableInfo;
    const originSum = this.initThs.reduce((pre, cur): number => parseFloat(cur) + pre, 0);
    const sum = this.ths.reduce((pre, cur, currentIndex): number => {
      if (currentIndex === lastColIndex) {
        return this.lvResizeMinWidth + pre;
      } else {
        return parseFloat(cur) + pre;
      }
    }, 0);
    this.ths[index] = `${width}px`;
    if (sum <= originSum) {
      this.ths[lastColIndex] = `${originSum - (sum - this.lvResizeMinWidth) - offset}px`;
    }
    this.viewportRefresh$.next(true);
    this.reflowScrollView();
  }

  /**
   * 设置当前拖拽列的下一个可拖拽列信息：index、初始宽度，初始两列的宽度
   */
  private setNextResizableCol(startIndex, resizeColWidth): void {
    const thComponents = this.thComponents.toArray();
    let index = startIndex + 1;
    for (; index < thComponents.length; index++) {
      const item = thComponents[index];
      if (item.isResizable) {
        if (item.elementRef.nativeElement.tagName !== 'TH') {
          throw initHtmlStructureError();
        }
        this.resizableInfo.nextColIndex = index;
        this.resizableInfo.initNextColWidth = item.elementRef.nativeElement.getBoundingClientRect().width;
        this.resizableInfo.initResizeColsWidth = resizeColWidth + this.resizableInfo.initNextColWidth;
        return;
      }
    }
  }
}
